﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;

namespace Mul
{
    /// <summary>
    /// 
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }

    public class MulViewModel : INotifyPropertyChanged
    {
        /// <summary>
        /// 成绩
        /// </summary>
        public decimal Score { set => Setter(value); get => Getter<decimal>(); }
        /// <summary>
        /// 显示成绩
        /// </summary>
        public Visibility ShowScore { set => Setter(value); get => Getter<Visibility>(); }
        /// <summary>
        /// 显示烟花
        /// </summary>
        public Visibility ShowFireworks { set => Setter(value); get => Getter<Visibility>(); }

        /// <summary>
        /// 挑战级别
        /// </summary>
        public int ChallengeLevel { set => CalculateScore(value); get => Getter<int>(); }
        /// <summary>
        /// 总数
        /// </summary>
        public int TotalCount { set => Setter(value); get => Getter<int>(); }
        /// <summary>
        /// 失败计数
        /// </summary>
        public int FailureCount { set => Setter(value); get => Getter<int>(); }
        /// <summary>
        /// 成功计数
        /// </summary>
        public int SuccessCount { set => Setter(value); get => Getter<int>(); }
        /// <summary>
        /// 完成计数
        /// </summary>
        public int CompleteCount { set => CalculateScore(value); get => Getter<int>(); }

        public int AlternationA { set => Setter(value); get => Getter<int>(); }
        public int AlternationB { set => Setter(value); get => Getter<int>(); }
        public int AlternationC { set => Setter(value); get => Getter<int>(); }
        public int AlternationD { set => Setter(value); get => Getter<int>(); }
        public int MultiplierA { set => Setter(value); get => Getter<int>(); }
        public int MultiplierB { set => Setter(value); get => Getter<int>(); }
        public int? Product { set => Setter(value); get => Getter<int>(); }

        /// <summary>
        /// 是否可答题
        /// </summary>
        public bool Open { set => Setter(value); get => Getter<bool>(); }
        /// <summary>
        /// 每次答题限时秒数
        /// </summary>
        public int Second { set => Setter(value); get => Getter<int>(); }
        /// <summary>
        /// 答题信息
        /// </summary>
        public string? Infos { set => Setter(value); get => Getter<string>(); }

        /// <summary>
        /// 后台定时器
        /// </summary>
        private readonly DispatcherTimer timer;
        /// <summary>
        /// 乘法口诀列表
        /// </summary>
        private List<(int, int, int)> _multiplicationFormulaList;
        /// <summary>
        /// 当前口诀
        /// </summary>
        private (int multiplierA, int multiplierB, int product) _currentFormula;
        public MulViewModel()
        {
            timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1)};
            timer.Tick += DispatcherTimerTick;
            StartCommand = new MulCommand(Start);
            AlternationClickCommand = new MulCommand(AlternationClick);
            ChallengeLevelSelectedCommand = new MulCommand(ChallengeLevelSelected);
            _multiplicationFormulaList = new List<(int, int, int)>();
            ShowFireworks = Visibility.Hidden;
            ShowScore = Visibility.Hidden;
            ChallengeLevel = 15;
        }
        /// <summary>
        /// 启动命令
        /// </summary>
        public ICommand StartCommand { get; }
        private void Start()
        {
            if (_multiplicationFormulaList.Count > 0) return;
            _multiplicationFormulaList = GenerateMultiplicationFormulaList();
            TotalCount = _multiplicationFormulaList.Count;
            ShowFireworks = Visibility.Hidden;
            ShowScore = Visibility.Hidden;
            Infos = string.Empty;
            CompleteCount = 0;
            SuccessCount = 0;
            FailureCount = 0;
            Score = 0;
            BeginTopics();
        }
        /// <summary>
        /// 做题开始
        /// </summary>
        private void BeginTopics()
        {
            if (_multiplicationFormulaList.Count == 0)
            {
                if (Score > 0) ShowScore = Visibility.Visible;
                if (Score > 99) ShowFireworks = Visibility.Visible;
                return;
            }
            var index = Random.Shared.Next(0, _multiplicationFormulaList.Count);
            var (multiplierA, multiplierB, product) = _multiplicationFormulaList[index];
            var alternations = GenerateAlternations((multiplierA, multiplierB, product));
            _currentFormula = (multiplierA, multiplierB, product);
            _multiplicationFormulaList.RemoveAt(index);
            AlternationA = alternations[0];
            AlternationB = alternations[1];
            AlternationC = alternations[2];
            AlternationD = alternations[3];
            MultiplierA = multiplierA;
            MultiplierB = multiplierB;
            Second = ChallengeLevel;
            Product = null;
            
            timer.Start();
            Open = true;
        }
        /// <summary>
        /// 做题结束
        /// </summary>
        /// <param name="success"></param>
        private void EndTopics(bool success)
        {

            _ = success ? SuccessCount++ : FailureCount++;
            CompleteCount++;
            Open = false;
            timer.Stop();
        }
        /// <summary>
        /// 计算成绩
        /// </summary>
        /// <param name="value"></param>
        /// <param name="propertyName"></param>
        private void CalculateScore(int value, [CallerMemberName] string? propertyName = null)
        {
            Setter(value, propertyName);
            if (value <= 0) return;
            //Score = Math.Round(value * 1M / TotalCount * 100, 2);
            Score = Math.Round(SuccessCount * 1M / value * 100, 2);
        }
        /// <summary>
        /// 记录信息
        /// </summary>
        /// <param name="value"></param>
        private void FormulaLogger(string value)
        {
            Infos += $"\r\n{_currentFormula.multiplierA}×{_currentFormula.multiplierB}={_currentFormula.product}({value})";
        }
        /// <summary>
        /// 后台定时器回调方法
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DispatcherTimerTick(object? sender, EventArgs e)
        {
            if (Second > 0)
            {
                Second--;
            }
            else
            {
                FormulaLogger("超时");
                EndTopics(false);
                BeginTopics();
            }
        }


        /// <summary>
        /// 给定算式生成备选答案
        /// </summary>
        /// <param name="formula"></param>
        /// <returns></returns>
        private static int[] GenerateAlternations((int,int,int) formula)
        {
            var result = new int[4];
            var random = Random.Shared;
            var (multiplierA, multiplierB, product) = formula;
            var alternations = new List<int>
            {
                product,
                multiplierA + multiplierB,
                product % 11 == 0 ? product + 1 : product % 10 + 10 + product / 10,
                random.Next(0, 2) == 0 || product < 4 ? product + random.Next(1, 18) : product - random.Next(1, 4)
            };

            for (var i = 0; i < result.Length; i++)
            {
                var index = random.Next(0, alternations.Count);
                var value = alternations[index];
                alternations.RemoveAt(index);
                result[i] = value;
            }
            return result;
        }
        /// <summary>
        /// 生成乘法口诀列表
        /// </summary>
        /// <returns></returns>
        private static List<(int multiplierA, int multiplierB, int product)> GenerateMultiplicationFormulaList()
        {
            var result = new List<(int, int, int)>();
            for (var i = 1; i < 10; i++)
                for (var j = i; j < 10; j++)
                    result.Add((i, j, i * j));
            return result;
        }


        /// <summary>
        /// 备选点击命令
        /// </summary>
        public ICommand AlternationClickCommand { get; }
        private async void AlternationClick(object sender)
        {
            if (sender is int value)
            {
                if (_currentFormula.product == value)
                {
                    EndTopics(true);
                    Product = value;
                    await Task.Delay(400);
                }
                else
                {
                    EndTopics(false);
                    FormulaLogger($"{value}");
                }
            }
            BeginTopics();
        }
        /// <summary>
        /// 难度选择命令
        /// </summary>
        public ICommand ChallengeLevelSelectedCommand { get; }
        private void ChallengeLevelSelected(object sender)
        {
            if (int.TryParse($"{sender}", out var value))
            {
                ChallengeLevel = value;
            }
        }

        #region INotifyPropertyChanged
        /// <summary>
        /// INotifyPropertyChanged
        /// </summary>
        public event PropertyChangedEventHandler? PropertyChanged;
        /// <summary>
        /// 变更通知字段集合
        /// </summary>
        private readonly Dictionary<string, object?> fields = new();
        /// <summary>
        /// Setter
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <param name="propertyName"></param>
        private void Setter<T>(T value, [CallerMemberName] string? propertyName = null)
        {
            if (propertyName == null) throw new ArgumentNullException(nameof(propertyName));
            fields[propertyName] = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        /// <summary>
        /// Getter
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        private T? Getter<T>([CallerMemberName] string? propertyName = null)
        {
            if (propertyName == null) throw new ArgumentNullException(nameof(propertyName));
            return fields.ContainsKey(propertyName) ? (T?)fields[propertyName] : default;
        }
        #endregion
    }

    /// <summary>
    /// ActionCommand
    /// </summary>
    public class MulCommand : ICommand
    {
        private event EventHandler? CanExe;
        private readonly Action? _execute;
        private readonly Action<object>? _executeAction;
        public MulCommand(Action execute)
        {
            _execute = execute;
        }
        public MulCommand(Action<object> execute)
        {
            _executeAction = execute;
        }
        public void Execute(object? parameter)
        {
            if (_execute != null)
            {
                _execute();
            }
            else
            {
                _executeAction?.Invoke(parameter ?? new object());
            }
        }
        bool ICommand.CanExecute(object? parameter) => true;
        event EventHandler? ICommand.CanExecuteChanged { add => CanExe += value; remove => CanExe -= value; }
    }

}
